%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Module summary
%%
%% Main authors: 
%% Progress to usefulness: 
%%
%% Library with functions that are shared between different
%% Chord core modules. 
%%
%%%%%%%%%%%%%%%%%%%%%%%

-module(chord_lib).
-compile(export_all).

-include("db.hrl").

%%%%%%%%%%%%%%%%%%%%%
%%
%% Helper functions
%%
%%%%%%%%%%%%%%%%%%%%%

setnth(1, [], _New) -> error("chord: setnth: Reached past end of list");
setnth(1, [_|Rest], New) -> [New|Rest];
setnth(I, [E|Rest], New) -> [E|setnth(I-1, Rest, New)].

dbg_str(Msg, State) ->
    case State#chord.verbose of
      true -> io:format(Msg);
      false -> ok
    end.

dbg_format(Fmt, Params, State) ->
    case State#chord.verbose of
      true -> io:format(Fmt, Params);
      false -> ok
    end.

dbg_header(Msg, State) ->
    case State#chord.verbose of
      true ->
        io:format("####################################~n"),
        io:format("  ~s~n", [Msg]),
        io:format("####################################~n");
      false -> ok
    end.

dbg_msg(Msg, State) ->
    case State#chord.verbose of
      true -> io:format("Chord: Got message: ~s~n", [Msg]);
      false -> ok
    end.

dbg_msg_loc(Msg, State) ->
    case State#chord.verbose of
      true -> io:format("Chord: Got message locally: ~s~n", [Msg]);
      false -> ok
    end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Local functions for managing the state object
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Creates a clean finger table with slots
%% that can be filled with nodes. 
create_finger_table() ->
  lists:duplicate(get_M(), #node{}).

%% Creates the finger table for the first node, by 
%% setting all fingers to the identity node. 
fill_finger_table(Id) ->
  lists:duplicate(get_M(), Id).

%% Get the node for the ith finger in the finger table for node n.
get_finger(Number, State) ->
  Ft = State#chord.finger_table,
  lists:nth(Number, Ft).

%% Set the node for the ith finger in the finger table for node n.
%% Returns the updated state. 
set_finger(Number, Finger, State) ->
  Ft1 = State#chord.finger_table,
  Ft2 = setnth(Number, Ft1, Finger),
  State#chord{finger_table = Ft2}.

% Constructs a list with tuples of I, finger_start(I)
% and current node at finger_table[I]
test_ft(I, State) ->
    case I == 0 of
        true -> [];
        false ->
            [{I, finger_start(I, State), get_finger(I, State)}] ++
            test_ft(I-1,State)
    end.

get_position(State) ->
  (State#chord.identity)#node.position.
get_pid(State) ->
  (State#chord.identity)#node.pid.
get_node(State) ->
  (State#chord.identity)#node.node.

create_node(NodeName) ->
  case NodeName of
     nothing ->
        nothing;
     _ ->
        #node{ node = NodeName }
  end.

%% Creates the identity node record. 
create_identity() ->
  Pos = hash:hash(node()),
  #node{ position = Pos, pid = self(), node = node() }.

%% Returns a clean state with a clean finger table and 
%% a working identity node. 
create_state() ->
  Id = create_identity(),
  Ft = create_finger_table(),
  {ok, SetupType}  = application:get_env(db, chordsetup),
  {ok, LookupType} = application:get_env(db, chordlookup),
  {ok, Verbose} = application:get_env(db, verbose),
  SetupModule = case SetupType of
    aggressive -> chord_aggressive;
    stabilized -> chord_stabilized
  end,
  LookupModule = case LookupType of
    iterative -> chord_iterative;
    distributed -> chord_distributed
  end,
  #chord{ identity = Id, finger_table = Ft,
          setup = SetupModule, lookup = LookupModule, verbose = Verbose }.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Networking help functions
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

rem_query(To, Msg, State) ->
    case To#node.node == node() of
      true ->
        error("Attepting to call rem_query on ourself");
      false ->
        dbg_format("Chord: Sending query to node: ~p Query: ~p~n",
                  [To#node.node, Msg], State),
        Reply = gen_server:call({?NETWORK, To#node.node}, Msg),
        dbg_format("Chord: Reply (rem) was: ~p~n", [Reply], State),
        Reply
    end.

rem_query_cast(To, Msg, State) ->
    case To#node.node == node() of
      true ->
        error("Attepting to call rem_query on ourself");
      false ->
        dbg_format("Chord: Sending query to node: ~p Query: ~p~n",
                  [To#node.node, Msg], State),
        Reply = gen_server:cast({?NETWORK, To#node.node}, Msg),
        dbg_format("Chord: Reply (rem) was: ~p~n", [Reply], State),
        Reply
    end.

%%%%%%%%%%%%%%%%%%%%%
%%
%% Action wrappers
%%
%%%%%%%%%%%%%%%%%%%%%

%% Getters: 
%% Either performs the get on ourself, and return the value,
%% or call a remote node, and return the remote node's value. 

wrap_get_successor(State, To) ->
    case To#node.node == node() of
      true ->
        dbg_msg_loc("get_successor", State),
        get_finger(1, State);
      false ->
        rem_query(To, {get_successor}, State)
    end.

wrap_get_predecessor(State, To) ->
    case To#node.node == node() of
      true ->
        dbg_msg_loc("get_predecessor", State),
        State#chord.predecessor;
      false ->
        rem_query(To, {get_predecessor}, State)
    end.

wrap_find_successor(State, To, Id) ->
    case To#node.node == node() of
      true ->
        dbg_msg_loc("find_successor", State),
        LookupModule = State#chord.lookup,
        LookupModule:find_successor(Id, State);
      false ->
        rem_query(To, {find_successor, Id}, State)
    end.

wrap_closest_preceding_finger(State, To, Id) ->
    case To#node.node == node() of
      true ->
        dbg_msg_loc("closest_preceding_finger", State),
        closest_preceding_finger(Id, State);
      false ->
        rem_query(To, {closest_preceding_finger, Id}, State)
    end.

%% Setters: 
%% Either performs the update on ourself, and return the updated state,
%% or call a remote node, and return our old state. 

wrap_set_predecessor(State, To, Pred) ->
    case To#node.node == node() of
      true ->
        dbg_msg_loc("set_predecessor", State),
        State#chord{ predecessor = Pred };
      false ->
        rem_query(To, {set_predecessor, Pred}, State),
        State
    end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Local functions for logic, 
%% independent of state and identity
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Get the "bit depth" of the Chord identifier ring. 
%% Determines an upper limit on the number of nodes. 
get_M() ->
    {ok,HashSize} = application:get_env(db, hashsize),
    HashSize.

%% Converts N into a "modulo space".
%% rem is the remainder operation, and not the modulo we want.
modspace(N) ->
    intmath:modspace(N, intmath:pow(2, get_M())).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Local functions for logic, 
%% dependent on state and identity
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Calculates n.finger[k].start for the node n. 
finger_start(K, State) ->
  case (K < 1) or (K > get_M()) of
    true  -> error("Invalid finger index input to finger_start");
    false -> 
         N = get_position(State),
         dbg_format("Dbg: Finger calculation, N = ~B, K = ~B, M = ~B~n",
                   [N, K, get_M()], State),
         modspace(N + intmath:pow(2, K - 1))
  end.

%% Finds the finger from the finger table that precedes the
%% identifier Id, and is closest to Id. 
closest_preceding_finger(Id, State) ->
  dbg_format("closest_preceding_finger: Finding cpf for pos ~B~n", [Id], State),
  test_closest_preceding_finger(get_M(), Id, State).

test_closest_preceding_finger(0, _, State) ->
  State#chord.identity;
test_closest_preceding_finger(I, Id, State) ->
  Fin = get_finger(I, State),
  Fnp = Fin#node.position,
  Stp = get_position(State),
  
  %% Previous code 
  %% case ((Stp < Fnp) and (Fnp < Id) of
  case intmath:interval_check_exc_exc(Stp, Fnp, Id) of
    true  ->
      dbg_format("test_closest_preceding_finger: Success with i=~B ~p at ~B~n",
                [I, Fin#node.node, Fnp], State),
      Fin;
    false ->
      dbg_format("test_closest_preceding_finger: Fail with i=~B ~p at ~B~n",
                [I, Fin#node.node, Fnp], State),
      test_closest_preceding_finger(I - 1, Id, State)
  
  %% New attempt, three cases when the correct finger is found
%%  if (Fnp >= Stp) and (Id > Fnp)  and (Id >= Stp) -> Fin;
%%	(Fnp >= Stp)  and (Id =< Fnp) and (Id < Stp) -> Fin;
%%	(Fnp < Stp)   and (Id > Fnp)  and (Id < Stp) -> Fin;
%%	true -> test_closest_preceding_finger(I - 1, Id, State)
  end.

